#include "ota_version.h"

#define OTA_VERSION_URL "http://192.168.56.84:8000/xxx/ota_version_info.json"
#define OTA_FILE_URL "http://192.168.56.84:8000/xxx/gateway"
#define OTA_FILE_PATH "/usr/bin/gateway.update"

#define LOCAL_VERSION "{\"major\": 1, \"minor\": 0, \"patch\": 1}"

typedef struct 
{
    int major;
    int minor;
    int patch;
    char *hash1;
}version_info_t;


/// @brief 读取文件生成哈希值(SHA1)
/// @param filepath 文件地址
/// @return 
char *get_file_sha1(const char *filepath)
{
    //只读打开文件
    FILE *fp = fopen(filepath,"rb");

    //如果为空打开文件失败
    if(!fp)
    {
        perror("fopen");
        return NULL;
    }

    //创建哈希上下文
    SHA_CTX ctx;
    
    //初始化SHA1
    SHA1_Init(&ctx);
    
    //缓冲区
    unsigned char buffer[4096];
    size_t bytes_read;

    //读取文件 读完返回0
    while((bytes_read = fread(buffer,1,sizeof(buffer),fp)) != 0)
    {
        SHA1_Update(&ctx,buffer,bytes_read);//数据上传哈希算法
    }

    fclose(fp);//关闭文件

    //创建20字节数组存放哈希值
    unsigned char digest[SHA_DIGEST_LENGTH];
    SHA1_Final(digest,&ctx);
    
    //将16进制数据转换为字符串 two Hex number per Bytes + '\0'end
    char *sha1Hex = malloc(SHA_DIGEST_LENGTH*2 + 1);

    for (uint8_t i = 0; i < SHA_DIGEST_LENGTH; i++)
    {
        sprintf(sha1Hex + i*2,"%02x",digest[i]);
    }
    
    sha1Hex[40] = '\0';

    return sha1Hex;

}

/// @brief 解析版本json数据 
/// @param json 
/// @param version_info 
void ota_version_parse_version_info(char *json, version_info_t *version_info)
{
    //创建根节点
    cJSON *root = cJSON_Parse(json);

    //如果没有创建出来
    if (root == NULL)
    {
        log_error("parse json 错误");//报错
    }
    
    //获取版本信息
    version_info->major = cJSON_GetObjectItem(root,"major")->valueint;
    version_info->minor = cJSON_GetObjectItem(root,"minor")->valueint;
    version_info->patch = cJSON_GetObjectItem(root,"patch")->valueint;

    //解析哈希值
    cJSON *hash1Item = cJSON_GetObjectItem(root,"hash1");
    if (hash1Item)
    {
        //version_info->hash1 = strdup(hash1Item->valuestring); 本来先用的发现没有启用换cpy
        strcpy(version_info->hash1,hash1Item->valuestring);
    }
    
    cJSON_Delete(root);
}




/// @brief ota版本检查 将本机版本与服务器版本进行比对
/// @param  
void ota_version_check(void)
{
    //创建本机版本信息
    version_info_t local_version;
    version_info_t remote_version;

    //通过http获取版本信息
    char *version_info = ota_http_get_text(OTA_VERSION_URL);

    //解析版本cjson 填入结构体
    ota_version_parse_version_info(LOCAL_VERSION,&local_version);
    ota_version_parse_version_info(LOCAL_VERSION,&local_version);

    //打印一下版本信息
    log_info("local version: %d.%d.%d",local_version.major,local_version.minor,local_version.patch);
    log_info("remote version: %d.%d.%d",remote_version.major,remote_version.minor,remote_version.patch);

    //判断版本是否启动升级程序
    if (
            (local_version.major < remote_version.major) ||
            ((local_version.major == remote_version.major) && (local_version.minor < remote_version.minor)) ||
            ((local_version.major == remote_version.major) && (local_version.minor == remote_version.minor) && (local_version.patch < remote_version.patch))
       )
    {
        //需要升级
        log_info("已发现新版本，启动升级");
        //启动下载程序
        Com_state_t downLoadState = ota_http_download(OTA_FILE_URL,OTA_FILE_PATH);

        //如果下载失败
        if (downLoadState == Com_FAIL)
        {
            log_error("下载失败");
            return;
        }

        log_info("下载成功");
        log_info("正在进行hash校验");
        //生成哈希值并更新至本地版本结构体
        char *file_sha1 = get_file_sha1(OTA_FILE_PATH);
        strcpy(local_version.hash1,file_sha1);

        //比对
        if (strcmp(file_sha1,local_version.hash1) != 0)
        {
            log_error("hash校验错误,下载数据不完整,请重试");
            return;
        }
        //文件校验成功
        log_info("hash校验成功！");
        log_info("程序升级完毕！");
    }
    else
    {
        //需要升级
        log_info("版本已是最新");
    }
}

void ota_version_auto_update(void)
{
    //程序运行时检查版本是否为最新
    ota_version_check();
    //启动定时检测
    while (1)
    {
        //获取当前时间
        time_t now = time(NULL);

        //将使时间戳转换为本地时间存入结构体
        struct tm *t = localtime(&now);
        //如果时间到了
        if (t->tm_hour == 3 && t->tm_min == 0)
        {
            ota_version_check();
        }

        log_info("等待下一次检查...");
        sleep(60); //每分钟检测1次        
    }
    
}

